--- title: "02-Redis 核心数据结构与命令" created: 2026-08-31 tags: - 项目筑基 --- # Redis 核心数据结构与命令 > Redis 系统讲解第一篇:把五大类型的命令**讲全**,再补上"01 路线篇"没展开的键设计与底层编码对应关系。01 篇是"高频命令体感",这一篇是"全景地图"——忘了哪个命令,先来这里查。 ## 键设计:先定规范再写代码 Redis 只有 key-value 两层,**业务语义全靠 key 的命名约定**: ```text 业务域:对象:ID:字段 —— 冒号分层是社区通用惯例 user:1:profile # 用户 1 的资料(Hash) order:202608:count # 8 月订单计数(String/INCR) sign:user:1:202608 # 用户 1 的 8 月签到(Bitmap) ``` 三条红线: - **key 别太长也别太短**:太长占内存(key 本身也是数据),太短没语义。100 字节以内为佳。 - **警惕大 key**:单个 value 太大(如一个 10 万元素的 List、几 MB 的 String)——读写阻塞单线程、删除卡顿(用 `UNLINK` 异步删代替 `DEL`)、迁移卡顿。发现手段:`redis-cli --bigkeys`。 - **警惕热 key**:某个 key 被疯狂访问(秒杀商品)——单线程模型下它是瓶颈。解法:本地缓存兜一层、key 加随机后缀拆多份。 ## String:最全命令与场景 | 命令 | 作用 | 场景备注 | | --- | --- | --- | | `SET key val [EX 秒] [NX\|XX] [KEEPTTL]` | 写入 | NX=不存在才写;XX=存在才写;KEEPTTL 保留原 TTL | | `GET` / `MGET` / `MSET` | 读 / 批量读 / 批量写 | MGET 一次网络往返拿 N 个,比循环 GET 快 | | `INCR` / `INCRBY` / `INCRBYFLOAT` | 原子自增 | 计数器、限流、分布式 id 雏形 | | `APPEND` / `STRLEN` | 追加 / 长度 | value 是字符串语义时用 | | `SETNX` | 不存在才写 | 老式锁写法,已被 `SET NX EX` 取代 | | `GETRANGE` / `SETRANGE` | 读子串 / 覆写子串 | 偏移量操作 | **注意**:Redis 的 String 是**二进制安全**的——存图片字节、序列化后的 JSON 都行,上限 512MB。 **Hash** ```bash HSET user:1 name "张三" age 21 # 多 field 一次写 HGET / HMGET / HGETALL # 单取 / 多取 / 全取 HDEL field / HKEYS / HVALS / HLEN # 删 field / 全部 field / 全部 value / 长度 HINCRBY user:1 age 1 # field 级原子自增——对象内计数很顺手 ``` 场景:对象缓存(比整个 JSON 塞 String 好改单字段)、购物车(field=商品 id,value=数量)。 **List** ```bash LPUSH / RPUSH / LPOP / RPOP # 双端进出 LRANGE key 0 -1 # 全量;负数下标从尾数 LLEN / LINDEX / LSET / LTRIM # 长度 / 按下标取 / 按下标赋值 / 修剪保长度 BLPOP / BRPOP key 5 # 阻塞弹出,5 秒等不到才放弃 ``` 场景:最新消息列表(LTRIM 保留前 N 条=固定窗口)、简单队列。 **Set** ```bash SADD / SREM / SISMEMBER / SCARD # 增 / 删 / 判存 / 计数 SMEMBERS # 全部成员——大集合慎用,用 SSCAN 渐进 SINTER / SUNION / SDIFF # 交 / 并 / 差(共同关注、推荐) SRANDMEMBER / SPOP # 随机看不取 / 随机弹出 ``` **ZSet** ```bash ZADD rank 95 "张三" # score member ZINCRBY / ZSCORE / ZCARD # 加分 / 查分 / 总数 ZREVRANGE rank 0 9 WITHSCORES # 排行榜 top10 ZRANK / ZREVRANK # 正序 / 逆序排名 ZRANGEBYSCORE rank 90 (100 WITHSCORES # 区间查询,( 表示开区间 ZREMRANGEBYSCORE rank -inf 60 # 删 60 分以下(定期清理老数据常用) ``` ZSet 是 Redis 最"值钱"的结构:排行榜、延迟队列(score 存执行时间戳)、滑动窗口限流都靠它。 ## 通用命令与键管理 ```bash SCAN 0 MATCH user:* COUNT 100 # 渐进遍历,生产唯一正确姿势 EXPIRE / PEXPIRE / TTL / PTTL # 秒级 / 毫秒级过期与查看 PERSIST key # 去掉 TTL OBJECT ENCODING key # 看底层编码(见下表) TYPE key # 看类型 RANDOMKEY / DBSIZE / FLUSHDB # 随机 key / 库大小 / 清库 ``` ## type 与 encoding:同一类型多种底层实现 `TYPE` 是对外逻辑类型,`OBJECT ENCODING` 是底层真实结构——**Redis 会按数据大小自动切换编码**,这是它省内存的秘诀之一: | TYPE | 小数据编码 | 大数据编码 | | --- | --- | --- | | string | int(整数)/ embstr(短字符串) | raw(长字符串) | | hash | listpack(field 少且短) | hashtable | | list | listpack | quicklist(双向链表套 listpack) | | set | intset(全整数时) | listpack / hashtable | | zset | listpack | skiplist + dict 双结构 | > 💡 阈值由配置里的 `*-max-listpack-entries / *-max-listpack-value` 控制。记忆钩子:**Redis 总是先用紧凑结构省内存,超阈值自动升级**——理解这一点,"为什么我的 hash 存了 130 个字段变慢"这类问题就有了方向。 ## 键过期与淘汰的完整图景 - 过期删除:**惰性删除**(访问时检查)+ **定期删除**(后台抽样清理)组合 - 内存淘汰:内存达到 `maxmemory` 后按 `maxmemory-policy` 淘汰——noeviction(默认,写报错)/ allkeys-lru / volatile-lru / allkeys-lfu / volatile-lfu / allkeys-random / volatile-random / volatile-ttl - 我的选择锚点:纯缓存选 `allkeys-lru`(或热点更集中的 `allkeys-lfu`);key 里混着"不能丢"的数据才用 `volatile-*`(只淘汰带 TTL 的) --- ⬅️ [[01-Redis 学习路线|01-Redis 学习路线]] 🏠 [[00-数据库|00-数据库]] ➡️ [[03-Redis 应用实战|03-Redis 应用实战]]